<?php
/**
 * MVC System used to sepparate code from the layout/views, and also organize things much better
 * A new way to run code for the CargoClaims application.
 *
 * @author Highstrike (highstrike@gmail.com)
 * @author Wavetrex (wavetrex@gmail.com)
 * @package CargoClaims
 * @version 1.0.dev
 */
class MVC_Core
{
	const
		DEBUG_DISABLED			= 0,
		DEBUG_CRITICAL			= 1,
		DEBUG_WARNING			= 2,
		DEBUG_ALL				= 3
		;

	/**
	 * The decoded "Settings" file
	 * @var array
	 */
	public static $settings = array();

	/**
	 * The currently used settings file ( will be overwritten by self::save_Settings() )
	 * @var string
	 */
	public static $settings_file;

	/**
	 * The translated debug level ( 0 - 3 )
	 *
	 * @var int
	 */
	public static $debug_level;

	/**
	 * The Sypc object used to load and dump config files.
	 * @var Spyc
	 */
	public static $spyc;

	/**
	 * Initializes the MVC core by setting up paths, loading configuration, etc.
	 */
	public static function init($settings_file = 'settings.yaml')
	{
		// allow loading libraries easy
		set_include_path(get_include_path().
			PATH_SEPARATOR. ROOT. DS.
			PATH_SEPARATOR. ROOT. DS. 'lib'
		);

		spl_autoload_register(array(__CLASS__, 'autoload'));
		set_exception_handler(array(__CLASS__, 'exception_handler'));

		self::$settings_file = $settings_file;
		self::$spyc = new Spyc(); // needed to load the $settings_file
		self::$settings = self::$spyc-> loadFile(ROOT .DS. 'cfg'. DS. self::$settings_file);

		// setup debug system
		if(isset(self::$settings['core']['debug'])) {
			$debug_level = self::$settings['core']['debug']['level'];
			$cname = "MVC_Core::DEBUG_{$debug_level}";
			if(defined($cname)) {
				self::$debug_level = constant($cname);
				if(self::$debug_level > 0) {
					ini_set('display_errors', 'stdout');
					error_reporting(-1);
				}
			}
		}

	}

	/**
	 * Saves the settings file
	 * @param string $settings_file Optionally, specify a custom settings_file in which the data is saved
	 */
	static public function save_Settings($settings_file = null)
	{
		// add some comments inside the settings file, to be easier to see when editing manually
		// TODO: Move this somewhere else, and replace the code here by a foreach(), in order to keep the core.php clean
		$settings_yaml = str_replace(
			array(
				"---\n",
				"\ncore:\n",
				"\ndatabase:\n",
				"\nhtml:\n",
				"\nlanguages:\n",
				"\nmainmenu:\n"
			),
			array(
				"# The new Settings system for CargoClaims App\n---\n",
				"\n# Core settings go directly here\ncore:\n",
				"\n\n# Database connection related settings\ndatabase:\n",
				"\n\n# Settings related to the visual appearance of the application (these go into the output HTML)\nhtml:\n",
				"\n\n# Available languages (These can be regenerated by analysing the language folders)\nlanguages:\n",
				"\n\n# Contains the definition of the main menu\nmainmenu:\n"
			),
		self::$spyc-> dump(self::$settings));

		file_put_contents(ROOT .DS. (is_null($settings_file)?self::$settings_file:$settings_file), $settings_yaml);
	}

	/**
	 * Autoloader system, used to locate files in the system by simply knowing their class
	 * @param unknown_type $class
	 */
	static public function autoload($class)
	{
		// Attempt to locate the class in the folder system using prefixes
		$class_frag = explode('_', strtolower($class));
		$location = implode($class_frag, DS) . '.php';
		if(self::real_path($location)) {
			require_once $location;
			return;
		}

		// Failed, throw an exception
		throw new MVC_Exception("Core Autoloader was unable to locate the class {$class}");
	}

	/**
	 * Exception handler that displays a nice output when the system crashes.
	 *
	 * @param MVC_Exception $exception
	 */
	static public function exception_handler(MVC_Exception $exception)
	{
		$CFG = new stdClass();
		$CFG-> paths = array('templates'=> array(
			ROOT. DS. 'app'. DS. 'view'
		));
		$drytpl = new Drytemplate($CFG);
		$out = array(
			'e'=> &$exception,
			'debug_mode'=> (bool)self::$debug_level,
		);
		if(self::$debug_level == 0)
			ob_end_clean(); // remove previous output, a partial page should not be visible in production mode
		else
			$out['until_crash'] = ob_get_level()>1?ob_get_clean():null;

		echo $drytpl-> render_File("exception.html", $out);
		die;
	}

	/**
	 * Returns the real path of a file name ( if absolute or relative )
	 *
	 * @param string $filename
	 */
	public static function real_path($filename)
	{
		// Check for absolute path
		if (realpath($filename) == $filename)
			return $filename;

		// Otherwise, treat as relative path
		$paths = explode(PATH_SEPARATOR, get_include_path());
		foreach($paths as $path) {
			if(substr($path, -1) == DS) $fullpath = $path . $filename;
			else $fullpath = $path . DS . $filename;

			if (file_exists($fullpath))
				return $fullpath;
		}

		return false;
	}

	/**
	 * Returns a controller object
	 * @param string $name Which controller? (After Rewrite and Router is implemented, this name will come from the router. For now, it's specified directly in the landing script
	 */
	public static function factory_Controller($name)
	{
		$class = 'App_Ctrl_' . ucfirst(strtolower($name));
		$controller = new $class();
		return $controller;
	}
}